A controller implements the interactive behavior for a view class. It is an object which transforms controller messages into model or view transformations.
A controller message is a message which is sent along exactly one path in a view hierarchy, the focus path. Every view on such a path decides for itself whether it is the terminal of this path, i.e. whether it is the current focus, or whether the message should be forwarded to one of its embedded views. Oberon supports two focus paths, namely a target and a front focus. Both paths may fall together into one. The target path defines which view is the target of commands in dialogs. The front path defines which view is being edited, via mouse, keyboard, or menus.
Picture 5.8b Simplified Example of Focus Hiearchy
It is important to note that all controller messages which are not relevant for a particular view type can simply be ignored.
Actions are objects whose Do procedures are executed in a delayed fashion, when the system is idle. An action which re-installs itself whenever it is invoked operates as a non-preemptive background task.
CONST now
This value can be passed to DoLater as notBefore parameter, if the action may be executed immediately.
CONST frontPath
This value may be passed as target parameter to several procedures of this module. It lets a focus message be sent along the front focus path.
CONST targetPath
This value may be passed as target parameter to several procedures of this module. It lets a controller message be sent along the target focus path.
CONST decLine, incLine, decPage, incPage
These values can be assigned to the ScrollMsg.op field. They will cause the receiver view to scroll by increments or decrements of one line or page. It is up to the receiver to define what constitutes a "line" in its model, except that it should be smaller than a page. A page should correspond to the width/height of the focus frame.
CONST gotoPos
This value can be assigned to the ScrollMsg.op field. It causes the receiver view to scroll to the position given by ScrollMsg.pos.
CONST nextPageX, nextPageY
This value can be assigned to the PageMsg.op field. It causes the receiver view to display the next page in x resp. in y direction.
CONST gotoPageX, gotoPageY
This value can be assigned to the PageMsg.op field. It causes the receiver view to display a given page in x resp. in y direction.
CONST cut, copy
These values can be assigned to the EditMsg.op field. They should be handled by the receiver view in the following way:
- cut delete selection, and assign a new view containing the deleted data or a copy thereof to EditMsg.view
- copy copy selection, and assign a new view containing the copied data to EditMsg.view
CONST pasteChar, pasteLChar
These values can be assigned to the EditMsg.op field. They denote the input of a CHAR value, or of a LONGCHAR value, respectively.
CONST paste
This value can be assigned to the EditMsg.op field. The receiver view should insert a copy of the data that EditMsg.view contains. If it cannot insert the data directly because it doesn't know its type, it should insert a copy of the whole EditMsg.view into its model if it has this capability, i.e. if it is a container.
CONST pasteView
This value can be assigned to the EditMsg.op field. The receiving container view should insert a copy of the whole EditMsg.view into its model.
CONST doubleClick
This value means that a mouse button has been pressed in a way which is interpreted by the underlying user interface as a "double click".
CONST extend, modify
Oberon operates with two virtual modifier keys. The extend key is used to extend or toggle selections (usually the Shift key), the modify key to change the default behavior of a command (e.g. to change a drag-and-move into a drag-and-copy, using the option key on the Macintosh). The behavior of possible additional modifier keys is platform-specific.
CONST noMark, mark
Used internally.
TYPE LONGCHAR
Type for 2-byte characters in the Unicode character set.
TYPE Controller
Interface, Extension
A controller is the third component of the model-view-controller triple. It's main purpose is to translate controller messages into model or view transformations. Simple applications can implement the controller's functionality in the view itself, thus needing no separate controller object at all. Container views usually have a separate controller.
ThisFocus is called to determine which view, if any, is currently focus. Typically this happens in a container view's Restore procedure before InstallFrame is called.
ThisFocus should be extended by a container controller, otherwise it returns NIL as default.
Usually there is a controller directory in the views module of a subsystem, since the view directory must be able to allocate an appropriate controller. Such a directory is installed by the corresponding controller module upon loading. To guarantee the loading of the corresponding controller module is loaded, the view module's body usually contains a statement of the form Dialog.Call("XYZControllers.Install, "", res).
PROCEDURE (d: Directory) New (): Controller
Interface
Returns a new controller.
result # NIL
result.init
result.ThisView() = NIL
TYPE Forwarder
Interface
Used internally.
PROCEDURE (n: Forwarder) Forward (target: BOOLEAN; VAR msg: Message)
Interface
Used internally.
PROCEDURE (n: Forwarder) Transfer (VAR msg: TransferMsg)
Interface
Used internally
TYPE Action
Interface
Actions are objects whose Do procedures are called in a deferred way, when the system is idle again.
notBefore-: LONGINT notBefore >= now
The earliest possible time when the action's Do procedure may be called.
PROCEDURE (a: Action) Do
Interface
For a registered action a, a.Do is called eventually after Ticks() has reached t.notBefore, or if t.notBefore = now.
TYPE Message
Interface
Base type of all controller messages. In contrast to model and view messages, a controller message is never broadcast. Instead, it is passed along a focus path. The target and front focus paths are predefined.
TYPE PollFocusMsg
Extension
This message is sent to find out the leaf view of a focus path. The message is handled by the framework itself. If your view should not give away its identity when it is focus, set focus to NIL. Otherwise ignore the message.
focus: View
After the message returns from the traversal of the focus path, it should contain the leaf view of the path.
TYPE PollSectionMsg
Extension
This message is sent to poll the focus view's current scroll state. Oberon/F contains a generic scrolling mechanism which scrolls simply by changing the scrolled frame's origin. Explicit handling of scrolling is only necessary for views which can become extremely large, and whose efficient implementation crucially depends on keeping frames small.
focus: BOOLEAN
This flag tells whether a container should forward the message to its focus or not. Non-containers obviously cannot forward to a focus.
vertical: BOOLEAN
Tells whether the vertical or horizontal direction is polled.
wholeSize: LONGINT wholeSize >= 1
This value denotes the focus view's width or height, in coordinates which a view can freely choose, i.e. they need not necessarily be universal units.
partSize: LONGINT 0 <= partSize <= wholeSize
This value denotes the focus view frame's width or height, in the same coordinates as above.
partPos: LONGINT 0 <= partPos <= wholeSize
This value denotes the focus view's origin, in the same coordinates as above.
valid: BOOLEAN
The receiving view should set this flag if it supports scrolling in the given direction. valid indicates that wholeSize, partSize, and partPos are valid indicators of the view's scroll position.
done: BOOLEAN
This flag should be set if the message has been interpreted, i.e. if the above output fields have been set. For some controllers this may depend on op, e.g. if the controller supports horizontal or vertical scrolling only.
TYPE PollOpsMsg
Extension
This message is sent to inquire which editing operations the focus view supports, depending on its current selection.
type: Stores.TypeName
This field denotes a context for the focus view. This context is used to determine which menus are relevant for the focus view. As a convention, a view assigns the type name of its abstract base type to type, e.g. "TextViews.ViewDesc". If the view doesn't support any such context, ignore this field.
singleton: Views.View
A container view which supports a selection should set this field to the selected view, if this view is the only contents currently selected.
pasteFrom: Views.View valid iff op IN {paste, pasteView}
The view of which a copy would be pasted, if a paste operation occured.
selectable: BOOLEAN
This field should be set to TRUE if the focus view contains selectable elements, independent of whether they are currently selected or not.
selectable: BOOLEAN
This field should be set to TRUE if the focus view contains selected elements which could be deselected.
valid: SET
This set denotes which edit operations (i.e. cut .. pasteView) are currently possible.
TYPE ScrollMsg
Extension
This message is sent in order to let the focus view scroll to another position. It is used only in conjunction with PollSectionMsg (cf. above).
focus: BOOLEAN
This flag tells whether a container should forward the message to its focus or not. Non-containers obviously cannot forward to a focus.
vertical: BOOLEAN
Denotes whether scrolling should occur in the vertical or in the horizontal direction.
op: INTEGER op IN {incLine..gotoPos}
Scroll operation to be performed.
pos: LONGINT valid iff op = gotoPos pos >= 0
This denotes the position to be scrolled to, in the same coordinates that the PollSectionMsg uses.
done: BOOLEAN
This flag should be set if the message has been interpreted; for some controllers, this may depend on op.
TYPE PageMsg
Extension
A page message is similar to a scroll message, but measures are in pages. It can be interpreted if the view should behave differently depending whether it is being printed or not. This is done e.g. in TextViews to avoid the last line on a page to become clipped, which is acceptable on screen.
If this message is not interpreted for a view which cannot be printed on one page, a default printing strategy is used which decomposes the view into suitable pieces for printing.
op: INTEGER op IN {nextPageX, nextPageY, gotoPageX, gotoPageY}
Where to scroll to.
pageX, pageY: INTEGER
Current page in x and in y directions.
done: BOOLEAN
This flag should be set if the message has been interpreted; for some controllers, this may depend on op.
eox, eoy: BOOLEAN
These flags should be set when it is attempted to go beyond the last page in the x resp. in y direction.
TYPE TickMsg
Extension
This message is sent to the front focus view periodically. It can be used to realize effects like a blinking caret.
tick: LONGINT
Tick count. The difference between two ticks is given by the global variable resolution.
TYPE MarkMsg
Extension
This message is sent whenever the target or front focus paths change. Before the change, the message is sent along the focus path with show = FALSE, such that the focus view can switch off visible marks like the selection or the caret. After the change (e.g. another window coming to the top), a MarkMsg is sent along the focus path with show = TRUE, such that the focus view can switch on its marks again, if there are any.
show: BOOLEAN
Tells whether the focus view's marks should be switched on or off.
TYPE SelectMsg
Extension
This message is sent when the focus view should select all selectable items, or when it should deselect all selected items.
set: BOOLEAN
Determines whether everything should be selected (set = TRUE) or whether everything should be deselected (set = FALSE).
TYPE RequestMessage
Interface, Extension
A view (or its controller) receiving a request message can indicate, by setting requestFocus to TRUE, that it wants to become focus afterwards, if it isn't already.
requestFocus: BOOLEAN
Set this flag to TRUE if receiver should become/remain focus after the message has been handled.
TYPE EditMsg
Extension
This message is sent when a key was pressed, or when a cut/copy/paste operation was invoked. The following operations can be supported (for every supported operation, the corresponding flag in PollOpsMsg.valid should be set when receiving a PollOpsMsg):
- cut
Create a clone of the focus view, with a copy of its selection as contents. Assign this clone to EditMsg.view and delete the focus' selection. There is one exception: if the selection consists of exactly one view (a singleton), this view's clone should be copied to EditMsg.view, not a clone of its container.
- copy
Create a clone of the focus view, with a copy of the focus' selection as contents. Assign this clone to EditMsg.view. If the selection consists of exactly one view (a singleton), this view's clone should be copied to EditMsg.view.
- pasteChar
Interpret EditMsg.char, e.g. insert it into a text model.
char may be a control character (-> "Appendix"). For control characters, check field modifiers also.
- pasteLChar
Interpret EditMsg.lchar, e.g. insert it into a text model.
- paste
Insert a copy of the contents of the EditMsg.view view into the focus view's contents. If the contents is not known to the focus view and if the focus is a container, it should insert a copy of the whole view (perform a pasteView operation, in fact).
- pasteView
Insert a copy of the EditMsg.view view into the focus view' contents, as a subview. The view's desired width and height are given in w and h.
op: INTEGER op IN {cut .. pasteView}
Operation to be performed.
modifiers: SET valid iff op = pasteChar & char is control character
Modifier keys.
char: CHAR valid iff op = pasteChar
Character to be pasted, or to be interpreted in the case of control characters.
lchar: LONGCHAR valid iff op = pasteLChar
Long character to be pasted.
view: Views.View valid iff op IN {paste, pasteView}
View of which a copy should be pasted.
w, h: LONGINT valid iff op IN {paste, pasteView}
w >= Views.undefined & h >= Views.undefined [units]
The desired width and height of the pasted view. These values can be treated as hints. If they are not suitable, others can be used. The value Views.undefined should be handled also.
TYPE ReplaceViewMsg
Extension
A container should check whether it contains old. If so, it should replace old by new, without modifying the view's context in any way.
old, new: Views.View old # NIL & new # NIL
old should be replaced by new.
TYPE CursorMessage
Interface, Extension
The base type of all messages which denote some interaction that depends on the current cursor position.
x, y: LONGINT
Current cursor position.
TYPE PollCursorMsg
Extension
This message is sent regularly to inquire which cursor the focus view currently desires.
cursor: INTEGER cursor IN {Ports.arrowCursor .. Ports.bitmapCursor}
This field can be set to the cursor appropriate for the focus view.
TYPE TrackMsg
Extension
This message is sent when the mouse button is pressed down.
modifiers: SET
Determines which modifier keys have been pressed together with the mouse button.
TYPE TransferMessage
Interface, Extension
This is the base type of all messages which denote an interaction between several views, e.g. for drag and drop.
source: Views.Frame
The frame from which the interaction started, e.g. where the mouse button has been clicked for dragging.
sourceX, sourceY: LONGINT
The position in the source frame where the mouse button has been clicked, e.g. when starting to drag.
TYPE PollDropMsg
Extension
While an item is being dragged around, PollDropMsgs are sent to enable feedback about the drop target.
mark: BOOLEAN
A container which supports drop feedback should show (hide) its feedback marked when mark is set (cleared).
show: BOOLEAN valid iff mark
Indicates whether the mark should be shown or hidden.
view: Views.View
The candidate for dropping.
dest: Views.Frame
The receiver should set dest to its own frame, if it would accept a drop.
TYPE DropMsg
Extension
This message is used if a view should be dragged and dropped to the cursor location.
view: Views.View
The view which is dropped. The view is the original, thus its contents must be copied first. The view itself must not be modified.
w, h: LONGINT [units]
The size of the dropped view.
VAR resolution-: LONGINT
Time resolution. A value of 60 for resolution means that the difference between time = n and time = n + 1 is 1/60 second. The current time can be inquired by procedure Ticks.
VAR path-: BOOLEAN
Used internally.
PROCEDURE Forward (VAR msg: Message)
Send msg to to current focus.
PROCEDURE FocusFrame (): Views.Frame
Returns the current focus frame, if there is any.
PROCEDURE FocusView (): Views.View
Returns the current focus view, if there is any.
PROCEDURE FocusModel (): Models.Model
Returns the current focus view's model, if there is any.
PROCEDURE DoLater (a: Action; notBefore: LONGINT)
Register an action. Its Do procedure will be executed once, later when time permits. If it should be executed more than once, it may call DoLater to re-install itself. A particular action can only be registered once; additional attempts will have no effect.
a # NIL 20
PROCEDURE Step
Used internally.
PROCEDURE Ticks (): LONGINT
Returns the current time in clock ticks. These ticks have a resolution of resolution., i.e. resolution ticks per second.
The following procedures are used internally:
PROCEDURE Register (f: Forwarder)
Add forwarder f to the list of forwarders. If f is already registered, Register(f) does nothing.
f # NIL 20
PROCEDURE Delete (f: Forwarder)
Remove f from the list of forwarders. If f is not registered, nothing happens.
f # NIL 20
PROCEDURE ForwardVia (target: BOOLEAN; VAR msg: FocusMessage)
Send msg to either target or front focus.
PROCEDURE SetCurrentPath (target: BOOLEAN)
Set path to target.
PROCEDURE PollSection (VAR msg: PollSectionMsg)
Poll the current focus view's scroll state.
PROCEDURE PollOps (VAR msg: PollOpsMsg)
Poll the current focus view's currently valid editing operations.
PROCEDURE PollCursor (x, y: LONGINT; VAR cursor: INTEGER)
Poll the current focus view's currently desired cursor.
PROCEDURE Transfer (x, y: LONGINT; source: Views.Frame; sourceX, sourceY: LONGINT;